<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - framebuffer - texture</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
		<style>
			#selection {
				position: fixed;
				display: flex;
				flex-direction: column;
				justify-content: center;
				align-items: center;
				height: 100%;
				width: 100%;
				top: 0;
				z-index: 999;
			}

			#selection > div {
				height: 128px;
				width: 128px;
				border: 1px solid white;
			}
		</style>
	</head>
	<body>

		<div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> framebuffer to texture
		</div>

		<div id="selection">
			<div></div>
		</div>

		<script type="module">

			import * as THREE from '../build/three.module.js';

			import { OrbitControls } from './jsm/controls/OrbitControls.js';
			import * as GeometryUtils from './jsm/utils/GeometryUtils.js';

			let camera, scene, renderer;
			let line, sprite, texture;

			let cameraOrtho, sceneOrtho;

			let offset = 0;

			const dpr = window.devicePixelRatio;

			const textureSize = 128 * dpr;
			const vector = new THREE.Vector2();
			const color = new THREE.Color();

			init();
			animate();

			function init() {

				//

				const width = window.innerWidth;
				const height = window.innerHeight;

				camera = new THREE.PerspectiveCamera( 70, width / height, 1, 1000 );
				camera.position.z = 20;

				cameraOrtho = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, 1, 10 );
				cameraOrtho.position.z = 10;

				scene = new THREE.Scene();
				sceneOrtho = new THREE.Scene();

				//

				const points = GeometryUtils.gosper( 8 );

				const geometry = new THREE.BufferGeometry();
				const positionAttribute = new THREE.Float32BufferAttribute( points, 3 );
				geometry.setAttribute( 'position', positionAttribute );
				geometry.center();

				const colorAttribute = new THREE.BufferAttribute( new Float32Array( positionAttribute.array.length ), 3 );
				colorAttribute.setUsage( THREE.DynamicDrawUsage );
				geometry.setAttribute( 'color', colorAttribute );

				const material = new THREE.LineBasicMaterial( { vertexColors: true } );

				line = new THREE.Line( geometry, material );
				line.scale.setScalar( 0.05 );
				scene.add( line );

				//

				const data = new Uint8Array( textureSize * textureSize * 3 );

				texture = new THREE.DataTexture( data, textureSize, textureSize, THREE.RGBFormat );
				texture.minFilter = THREE.NearestFilter;
				texture.magFilter = THREE.NearestFilter;

				//

				const spriteMaterial = new THREE.SpriteMaterial( { map: texture } );
				sprite = new THREE.Sprite( spriteMaterial );
				sprite.scale.set( textureSize, textureSize, 1 );
				sceneOrtho.add( sprite );

				updateSpritePosition();

				//

				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				renderer.autoClear = false;
				document.body.appendChild( renderer.domElement );

				//

				const selection = document.getElementById( 'selection' );
				const controls = new OrbitControls( camera, selection );
				controls.enablePan = false;

				//

				window.addEventListener( 'resize', onWindowResize );

			}

			function onWindowResize() {

				const width = window.innerWidth;
				const height = window.innerHeight;

				camera.aspect = width / height;
				camera.updateProjectionMatrix();

				cameraOrtho.left = - width / 2;
				cameraOrtho.right = width / 2;
				cameraOrtho.top = height / 2;
				cameraOrtho.bottom = - height / 2;
				cameraOrtho.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

				updateSpritePosition();

			}

			function updateSpritePosition() {

				const halfWidth = window.innerWidth / 2;
				const halfHeight = window.innerHeight / 2;

				const halfImageWidth = textureSize / 2;
				const halfImageHeight = textureSize / 2;

				sprite.position.set( - halfWidth + halfImageWidth, halfHeight - halfImageHeight, 1 );

			}

			function animate() {

				requestAnimationFrame( animate );

				const colorAttribute = line.geometry.getAttribute( 'color' );
				updateColors( colorAttribute );

				// scene rendering

				renderer.clear();
				renderer.render( scene, camera );

				// calculate start position for copying data

				vector.x = ( window.innerWidth * dpr / 2 ) - ( textureSize / 2 );
				vector.y = ( window.innerHeight * dpr / 2 ) - ( textureSize / 2 );

				renderer.copyFramebufferToTexture( vector, texture );

				renderer.clearDepth();
				renderer.render( sceneOrtho, cameraOrtho );

			}

			function updateColors( colorAttribute ) {

				const l = colorAttribute.count;

				for ( let i = 0; i < l; i ++ ) {

					const h = ( ( offset + i ) % l ) / l;

					color.setHSL( h, 1, 0.5 );
					colorAttribute.setX( i, color.r );
					colorAttribute.setY( i, color.g );
					colorAttribute.setZ( i, color.b );

				}

				colorAttribute.needsUpdate = true;

				offset -= 25;

			}

		</script>

	</body>
</html>
